package de.bloxel;

import java.util.Random;

import com.jme3.app.SimpleApplication;
import com.jme3.asset.TextureKey;
import com.jme3.audio.AudioNode;
import com.jme3.bounding.BoundingVolume;
import com.jme3.effect.ParticleEmitter;
import com.jme3.effect.ParticleMesh.Type;
import com.jme3.font.BitmapText;
import com.jme3.input.KeyInput;
import com.jme3.input.controls.ActionListener;
import com.jme3.input.controls.AnalogListener;
import com.jme3.input.controls.KeyTrigger;
import com.jme3.light.DirectionalLight;
import com.jme3.material.Material;
import com.jme3.math.ColorRGBA;
import com.jme3.math.Vector3f;
import com.jme3.scene.Geometry;
import com.jme3.scene.Node;
import com.jme3.scene.shape.Box;
import com.jme3.scene.shape.Sphere;
import com.jme3.texture.Texture;
import com.jme3.util.TangentBinormalGenerator;

/**
 * @author patrick
 * @author klaus
 * @author andreas
 */
public class Pong extends SimpleApplication {

	private enum DirectionBall {
		LEFT, RIGHT, UP, DOWN, NONE
	}

	/**
	 * Ein bissl Nebel fuer die Kugel wenn sie fliegt.
	 */
	private ParticleEmitter emit;

	private Node borderNode;

	private Node panelNode;

	private Geometry shiny_rock;

	private BoundingVolume boundGeometryLeftBorder;

	private BoundingVolume boundGeometryRightBorder;

	private BoundingVolume boundSphere;

	private BoundingVolume boundGeometryRightPanel;

	private BoundingVolume boundGeometryLeftPanel;

	private BoundingVolume boundGeometryBottomBorder;

	private BoundingVolume boundGeometryTopBorder;

	private Geometry geometryLeftPanel;

	private Geometry geometryRightPanel;

	private DirectionBall moveDirectionBall = DirectionBall.RIGHT;
	private DirectionBall moveDirectionBall2 = DirectionBall.DOWN;

	private int panelHit = 1;

	private int leftScore = 0;

	private int rightScore = 0;

	private BitmapText scroeText;

	private AudioNode panelHitAudio;

	private AudioNode goalAudio;

	public static void main(String[] args) {
		new Pong().start();
	}

	@Override
	public void simpleInitApp() {
		// enable physics
		// BulletAppState bulletAppState = new BulletAppState();
		// stateManager.attach(bulletAppState);

		geometryLeftPanel = createPanel(-5, 0, 0);
		geometryRightPanel = createPanel(5, 0, 0);
		boundGeometryLeftPanel = geometryLeftPanel.getWorldBound();
		boundGeometryRightPanel = geometryRightPanel.getWorldBound();

		panelNode = new Node("panels");
		panelNode.attachChild(geometryLeftPanel);
		panelNode.attachChild(geometryRightPanel);
		rootNode.attachChild(panelNode);

		createBall();

		// Must add a light to make the lit object visible!
		createLightSpot();

		// create field borderds
		createBorders();

		// init key bindings
		initKeys();

		// init audio
		initAudio();

		guiNode.detachAllChildren();
		guiFont = assetManager.loadFont("Interface/Fonts/Default.fnt");
		scroeText = new BitmapText(guiFont, false);
		scroeText.setSize(guiFont.getCharSet().getRenderedSize());
		scroeText.setLocalTranslation(100, scroeText.getLineHeight() + 25, 0);
		guiNode.attachChild(scroeText);

		// init camera
		cam.setLocation(new Vector3f(0, -4f, 10f));
		cam.lookAt(Vector3f.ZERO, new Vector3f(0, 1, 0));
		cam.setFrustumFar(15);
	}

	@Override
	public void simpleUpdate(final float tpf) {
		scroeText.setText(leftScore + ":" + rightScore);
		rootNode.updateGeometricState();
		if (boundSphere.intersects(boundGeometryRightBorder)) {
			emit.killAllParticles();
			System.out.println("Tor links!");
			leftScore++;
			moveDirectionBall = DirectionBall.NONE;
			moveDirectionBall2 = DirectionBall.NONE;
			// audioRenderer.playSource(goalAudio);
			reset();
		}
		if (boundSphere.intersects(boundGeometryLeftBorder)) {
			emit.killAllParticles();
			System.out.println("Tor Rechts!");
			rightScore++;
			moveDirectionBall = DirectionBall.NONE;
			moveDirectionBall2 = DirectionBall.NONE;
			// audioRenderer.playSource(goalAudio);
			reset();
		}
		if (boundSphere.intersects(boundGeometryRightPanel)) {
			moveDirectionBall = DirectionBall.LEFT;
			panelHit++;
			audioRenderer.playSource(panelHitAudio);
		}
		if (boundSphere.intersects(boundGeometryLeftPanel)) {
			moveDirectionBall = DirectionBall.RIGHT;
			panelHit++;
			audioRenderer.playSource(panelHitAudio);
		}
		if (boundSphere.intersects(boundGeometryTopBorder)) {
			moveDirectionBall2 = DirectionBall.DOWN;
		}
		if (boundSphere.intersects(boundGeometryBottomBorder)) {
			moveDirectionBall2 = DirectionBall.UP;
		}
		moveDirection(tpf);
	}

	private void createBall() {
		final Sphere ball = new Sphere(32, 32, 0.3f);
		shiny_rock = new Geometry("Shiny rock", ball);
		ball.setTextureMode(Sphere.TextureMode.Projected); // better quality on
		// spheres
		TangentBinormalGenerator.generate(ball); // for lighting effect
		final Material mat_lit = new Material(assetManager,
				"Common/MatDefs/Light/Lighting.j3md");
		mat_lit.setTexture("m_DiffuseMap",
				assetManager.loadTexture("Textures/Terrain/Pond/Pond.png"));
		mat_lit.setTexture("m_NormalMap", assetManager
				.loadTexture("Textures/Terrain/Pond/Pond_normal.png"));
		mat_lit.setFloat("m_Shininess", 5f); // [0,128]
		shiny_rock.setMaterial(mat_lit);
		shiny_rock.setLocalTranslation(0, 2, 0); // Move it a bit
		shiny_rock.rotate(1.6f, 0, 0); // Rotate it a bit
		boundSphere = shiny_rock.getWorldBound();

		emit = new ParticleEmitter("Emitter", Type.Triangle, 200);
		emit.setGravity(0);
		emit.setVelocityVariation(1);
		emit.setLowLife(1);
		emit.setHighLife(1);
		emit.setInitialVelocity(new Vector3f(0, .5f, 0));
		emit.setImagesX(15);
		Material mat = new Material(assetManager,
				"Common/MatDefs/Misc/Particle.j3md");
		mat.setTexture("Texture",
				assetManager.loadTexture("Effects/Smoke/Smoke.png"));
		emit.setMaterial(mat);
		emit.setLocalTranslation(0, 2, 0);

		rootNode.attachChild(shiny_rock);
		rootNode.attachChild(emit);
	}

	private void createBorders() {
		final Box leftBorder = new Box(new Vector3f(-6f, 0f, 0f), 0.001f, 3f,
				1f);
		final Geometry geometryLeftBorder = new Geometry("leftBorder",
				leftBorder);
		final Material matBorder = new Material(assetManager,
				"Common/MatDefs/Misc/SimpleTextured.j3md");
		TextureKey key = new TextureKey(
				"Textures/Terrain/BrickWall/BrickWall.jpg");
		key.setGenerateMips(true);
		Texture tex = assetManager.loadTexture(key);
		matBorder.setTexture("ColorMap", tex);
		geometryLeftBorder.setMaterial(matBorder);

		final Box rightBorder = new Box(new Vector3f(6f, 0f, 0f), 0.001f, 3f,
				1f);
		final Geometry geometryRightBorder = new Geometry("rightBorder",
				rightBorder);
		geometryRightBorder.setMaterial(matBorder);

		final Box topBorder = new Box(new Vector3f(0f, 3f, 0f), 5.9f, 0.001f,
				1f);
		final Geometry geometryTopBorder = new Geometry("topBorder", topBorder);
		geometryTopBorder.setMaterial(matBorder);

		final Box bottomBorder = new Box(new Vector3f(0f, -3f, 0f), 5.9f,
				0.001f, 1f);
		final Geometry geometryBottomBorder = new Geometry("bottomBorder",
				bottomBorder);
		geometryBottomBorder.setMaterial(matBorder);

		borderNode = new Node("borders");
		borderNode.attachChild(geometryLeftBorder);
		borderNode.attachChild(geometryRightBorder);
		borderNode.attachChild(geometryTopBorder);
		borderNode.attachChild(geometryBottomBorder);
		rootNode.attachChild(borderNode);

		boundGeometryLeftBorder = geometryLeftBorder.getWorldBound();
		boundGeometryRightBorder = geometryRightBorder.getWorldBound();
		boundGeometryTopBorder = geometryTopBorder.getWorldBound();
		boundGeometryBottomBorder = geometryBottomBorder.getWorldBound();
	}

	private void createLightSpot() {
		final DirectionalLight sun = new DirectionalLight();
		sun.setDirection(new Vector3f(1, 0, -2).normalizeLocal());
		sun.setColor(ColorRGBA.White);
		rootNode.addLight(sun);
	}

	private Geometry createPanel(final float x, final float y, final float z) {
		final Box panelBox = new Box(new Vector3f(x, y, z), 0.1f, 1f, 0.1f);
		final Geometry panel = new Geometry("Panel", panelBox);
		final Material matLeftPanel = new Material(assetManager,
				"Common/MatDefs/Misc/SolidColor.j3md");
		matLeftPanel.setColor("m_Color", ColorRGBA.Blue);
		panel.setMaterial(matLeftPanel);
		return panel;
	}

	private void initAudio() {
		panelHitAudio = new AudioNode(assetManager, "Sound/Effects/Bang.wav",
				false);
		panelHitAudio.setLooping(false);
		panelHitAudio.setVolume(0.2f);

		goalAudio = new AudioNode(assetManager, "Sounds/goal.wav", false);
		goalAudio.setLooping(false);
		goalAudio.setVolume(0.2f);
	}

	private void initKeys() {
		inputManager.addMapping("leftUP", new KeyTrigger(KeyInput.KEY_R));
		inputManager.addMapping("leftDOWN", new KeyTrigger(KeyInput.KEY_F));
		inputManager.addMapping("rightUP", new KeyTrigger(KeyInput.KEY_I));
		inputManager.addMapping("rightDOWN", new KeyTrigger(KeyInput.KEY_K));
		inputManager.addMapping("Restart", new KeyTrigger(KeyInput.KEY_RETURN));

		final AnalogListener analogListener = new AnalogListener() {

			@Override
			public void onAnalog(final String name, float value, final float tpf) {
				value = value * 5f;
				if (name.equals("leftUP")
						&& !boundGeometryLeftPanel
								.intersects(boundGeometryTopBorder)) {
					geometryLeftPanel.move(0, value * speed, 0);
				}
				if (name.equals("leftDOWN")
						&& !boundGeometryLeftPanel
								.intersects(boundGeometryBottomBorder)) {
					geometryLeftPanel.move(0, -value * speed, 0);
				}
				if (name.equals("rightUP")
						&& !boundGeometryRightPanel
								.intersects(boundGeometryTopBorder)) {
					geometryRightPanel.move(0, value * speed, 0);
				}
				if (name.equals("rightDOWN")
						&& !boundGeometryRightPanel
								.intersects(boundGeometryBottomBorder)) {
					geometryRightPanel.move(0, -value * speed, 0);
				}
			}
		};

		final ActionListener actionlistener = new ActionListener() {

			@Override
			public void onAction(final String name, final boolean isPressed,
					final float tpf) {
				if (name.equals("Restart") && isPressed) {
					moveDirectionBall2 = DirectionBall.DOWN;
					moveDirectionBall = DirectionBall.RIGHT;
				}
			}
		};
		inputManager.addListener(actionlistener, new String[] { "Restart" });
		inputManager.addListener(analogListener, new String[] { "leftUP",
				"leftDOWN", "rightUP", "rightDOWN" });
	}

	private void moveBall(final float x, final float y) {
		shiny_rock.move(x, y, 0);
		emit.move(x, y, 0);
		emit.setEnabled(moveDirectionBall != DirectionBall.NONE
				&& moveDirectionBall2 != DirectionBall.NONE);
	}

	private void moveDirection(final float tpf) {
		int noise = new Random().nextInt(panelHit+1);
		if (moveDirectionBall == DirectionBall.LEFT) {
			moveBall(-1 * tpf * noise, 0);
		}
		if (moveDirectionBall == DirectionBall.RIGHT) {
			moveBall(1 * tpf * noise, 0);
		}
		if (moveDirectionBall2 == DirectionBall.UP) {
			moveBall(0, tpf * 1 * noise);
		}
		if (moveDirectionBall2 == DirectionBall.DOWN) {
			moveBall(0, tpf * -1 * noise);
		}
		if (moveDirectionBall == DirectionBall.NONE) {
			moveBall(0, 0);
		}
		if (moveDirectionBall2 == DirectionBall.NONE) {
			moveBall(0, 0);
		}
	}

	private void reset() {
		shiny_rock.setLocalTranslation(0, 2, 0);
		emit.setLocalTranslation(0, 2, 0);
		panelHit = 1;
	}
}